iT邦幫忙

2022 iThome 鐵人賽

DAY 3
0

前言

昨天我們快速地看過作業系統需要達成的主要目標,包含抽象,多工等等。而在這一篇章,我們將了解作業系統的主要服務以及 xv6 作業系統的相關背景,以及 xv6 作業系統所提供的功能,以及支援的 System call,了解如何啟動 xv6 作業系統與使用 debug mode 來啟動 xv6。

xv6 作業系統

xv6 為 MIT 用於教學用的簡單作業系統 (6.828, 6S081 作業系統課程),xv6 為 UNIXv6 以及 x86 組合語言這兩個詞所結合而成,UNIXv6 為第一個對外公開的 UNIX 版本,由 Ken Thompson 和 Dennis Ritchie 所開發。在 2019 年後,6.828, 6S081 課程改成使用 RISC-V 版本的 xv6 進行教學。xv6 使用 ANSI C 以及組合語言進行開發。xv6 為 UNIX Like 的多核心 RISC-V 作業系統

xv6 的 kernel 部分有兩種實做,一種是為了 x86 架構而設計的,平台為多核心 x86,另外一種為使用 RISC-V 設計的,在 RISC-V 中,使用的是64位元的處理器。

在本文,將使用 RISC-V 實作 kernel 的 xv6,也就是讓 xv6 在RISC-V 架構的 CPU 上執行,理論上可以讓 xv6 在一台 RISC-V 的機器上執行,但是在本文,為了避免硬體架構帶來的不方便以及影響,將使用模擬器 QEMU 來模擬 RISC-V 架構並且執行 xv6。

xv6 所提供的功能

xv6 為共享記憶體多核心作業系統 (shared-memory multiprocessor),128 MB 的主記憶體是共享的。而 xv6 主要實作出了作業系統以下功能

  • process
  • virtual address space (page table)
  • File, 目錄結構
  • pipe
  • multitasking (time-slice)
  • 21 system call

多(處理器)核心系統 (multiprocessor system)

多核心系統,平行系統 (parallel),這種系統擁有多個處理器,並且共用匯流排以及時脈,記憶體,周圍 I/O 裝置等等,這種系統主要有以下優點

  1. 完成更多工作 : 藉由增加處理器的數量,理想上我們可以在較短的時間內完成較多的工作。但是實際上並不會我們多出了 N 個處理器,效率就提升了 N 倍,因為會存在彼此競爭的問題 (也就是所謂的 race condition),處理器之間彼此合作勢必會有一些資源是共享的,而當彼此使用共享資源,如果沒有使用一定的管理策略,諸如 lock 機制,則會造成資料的錯誤,但是使用 lock 卻會降低處理的效率。
  2. 增加系統可靠度 : 如果功能能夠被拆分給多個處理器執行時,當其中一顆處理器發生錯誤時,並不會造成整個程式無法完成,只會造成降低效率而已。

現代的電腦架構系統大致上可以使用下圖進行表示 :

<from Operating System Concept (9 th Edition)>

xv6 快速總覽

xv6 裝置

在後面介紹 kernel virtual address space 時,我們可以看到 xv6 所使用的 I/O 裝置 (也就是上圖中,device 的部分),這邊先大致上提及 :

  • UART (Universal Asynchronous Receiver/Transmitter) : 通用非同步收發器(沒有使用時脈進行對齊),為電腦硬體的一部分,將資料通過 serial 進行通訊。所謂的 serial 通訊指的是在通訊的時候一次傳送若干位元,並進行多次的通訊方式,有傳送方 Tx 與接收方 Rx (跟 Arduino 很像)。
  • DISK : 硬碟裝置,通過 QEMU 模擬的。
  • Timer Interrupt : 在每一個 hart 中都有稱為 LAPIC 的芯片,芯片中有 timer,可用於接收 timer interrupt。timer interrupt 由周邊裝置產生。
  • PLIC (Platform Level Interrupt Controller) : 為一個芯片或是電路,用來處理各種外部裝置發生的中斷,決定是否發生中斷以及修先級問題。
  • CLINT (Core Local Interrupt Controller) : 處理 timer, software Interrupt,並維護記憶體的映射以及 CSR (control and status register)。

xv6 記憶體管理

記憶體管理需要完成紀錄目前記憶體資源是由誰正在使用,以及決定哪一些 process 的資料需要移入或移出記憶體空間,和決定 process 配置以及回收記憶體空間,用於管理記憶體的單位稱為一個分頁 (page)。

  • 分頁大小為 4096,使用 PGSIZE 巨集定義。
  • 可用的記憶體分頁會使用 free list 進行管理,free list 為一單向練結串列的資料結構,而分頁的管理程式碼位於kalloc.c中。
  • 記憶體分頁使用三層式的樹狀結構進行組織。每一個 process 都有自己的 page table,而 kernel 自己也有一個 page table。

xv6 排程

  • 使用 Round-Robin
  • timeslice 的大小固定,為 1000000 cycles。
  • 所有的核心(hart, core) 共享同一個 ready queue 進行排程。

xv6 平行處理與控制

  • 使用 spinlock。
  • 為了避免 spinlock 效率浪費(不斷的 spin),有 sleep(), wakup()

xv6 bootloader

  • 在 RISC-V xv6 中,並不關心 bootloader,這一部分會在後續提及。

正常啟動 xv6

環境: Ubuntu 20.04 LTS
安裝工具 : QEMU 5.1+, GDB 8.3+, GCC, and Binutils.

sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu 

git clone實驗環境 xv6-2021

git clone git://g.csail.mit.edu/xv6-labs-2021
cd xv6-labs-2021
git checkout util

啟動xv6

make qemu

使用Ctrl-A X即可退出。

可以看到我們的 xv6 是跑在一個稱為 QEMU 的模擬器上,而 QEMU 這個模擬器,我們可以將其簡單的看做下面裝置

< The HiFive Unleashed board >
我們應該想像成 xv6 跑在上面這一塊板子上。

xv6 debug 模式

QEMU 內部有一個 gdb server,我們可以通過 gdb 連線到該 server 去對 xv6 進行 debug,操作為在一個視窗使用 debug mode 啟動 xv6,接著會等待連線

make qemu-gdb


接著我們可以在另外一個視窗,啟動交互式 gdb (另一個視窗需要在 xv6 目錄底下)

gdb-multiarch


我們就能夠在左方的 gdb 視窗進行除錯,可以通過kernel.asm得知函式所在的記憶體地址,使用 gdb 對該地址下斷點進行除錯。

xv6結構

  • kernel資料夾中大多數為C的原始碼(*.c),C的標頭檔案(*.h),組合語言檔案(*.h),裡面的程式會執行在 kernel mode (supervisor mode 底下)
  • user資料夾中大多數為在user space (user mode) 執行的程式,像是在UNIX中常見的ls, cat等等,以及初始化process的組合語言程式。
  • Makefile 用來build整個專案,在上面我們啟動xv6時,便是通過make這個工具程式讀取Makefile自動化建構出整個專案。

Makefile 會去讀取 C 原始碼,接著通過編譯器,產生出 A.S,這是 RISC-V 格式的組合語言檔案,接著再通過組譯器,產生出A.O,目的檔為二進位格式的組合語言,而 Makefile 會對所有 C 原始碼進行這一些操作,接下來 Loader 會將這一些目的檔通過鏈結器產生出 kernel file,QEMU 會執行他,在課程中為了方便除錯,還會產生出kernel.asm,可以方便讓我們得知每一條指令的記憶體地址,通過 gdb 下斷點進行除錯。

User Program

如果要自行擴充 user program,需要將原始碼 .c 放置於 user 資料夾中,接著需要修改 Makefile 檔案,在UPROGS底下加入原始碼名稱,格式為U/_name\

xv6可使用的System call

/kernel/syscall.h

// System call numbers
#define SYS_fork    1
#define SYS_exit    2
#define SYS_wait    3
#define SYS_pipe    4
#define SYS_read    5
#define SYS_kill    6
#define SYS_exec    7
#define SYS_fstat   8
#define SYS_chdir   9
#define SYS_dup    10
#define SYS_getpid 11
#define SYS_sbrk   12
#define SYS_sleep  13
#define SYS_uptime 14
#define SYS_open   15
#define SYS_write  16
#define SYS_mknod  17
#define SYS_unlink 18
#define SYS_link   19
#define SYS_mkdir  20
#define SYS_close  21                 

System call 可以大概分成六大類型

  • 處理程序控制 (process control)
    • fork()
    • exit()
    • wait()
  • 檔案管理 (file manipulation)
    • open()
    • read()
    • write()
    • close()
  • 裝置管理 (device manipulation)
    • ioctl()
    • read()
    • write()
  • 資料維護 (information maintenance)
    • getpid()
    • alarm()
    • sleep()
  • 通信使用 (communication)
    • pip()
  • 保護 (protection)
    • chmod()

reference

xv6-riscv
Operating System Concepts, 9/e


上一篇
Day-01 作業系統概要
下一篇
Day-03 Process System call
系列文
與作業系統的第一類接觸 : 探索 xv631
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言